#pragma once

#include <iostream>
#include <memory>
#include <unistd.h>
#include <cstdlib>
#include <jsoncpp/json/json.h>
#include "LogMessage.hpp"

#define Customize = 1;

namespace Protocol
{
    const std::string space = " ";
    const std::string linebreaks = "\r\n";

    enum Err_Des
    {
        Success = 0,
        Divide_Zero,
        Modulo_Zero,
        Not_Recognized
    };

    // 添加报头
    void EnCode(std::string &input)
    {
        std::string str = std::to_string(input.size());
        str += linebreaks + input + linebreaks;
        input = str;
    }

    // 解析报头，并放在message中
    // le
    // len
    // len\n
    // len\nx
    // len\nx op
    // len\nx op
    // len\nx op y\n
    // len\nx op y\nlen..
    // 7\n12 + 32\n
    bool DeCode(std::string &package, std::string *message)
    {
        int pos = package.find(linebreaks);
        if (pos == std::string::npos)
            return false;

        std::string len = package.substr(0, pos);
        int messagelen = atoi(len.c_str());
        int total = len.size() + 2 * linebreaks.size() + messagelen;
        if (total > package.size())
        {
            return false;
        }

        *message = package.substr(pos + linebreaks.size(), messagelen);
        package.erase(0, total);

        return true;
    }

    class Request
    {
    public:
        Request()
        {
        }

        Request(int x, char oper, int y)
            : _data_x(x), _data_y(y), _oper(oper)
        {
        }

        //_data_x _oper _data_y
        void Serialization(std::string *out)
        {
#ifdef Customize
            *out = std::to_string(_data_x) + space + _oper + space + std::to_string(_data_y);
            return;
#else
            Json::Value root;
            root["datax"] = _data_x;
            root["datay"] = _data_y;
            root["oper"] = _oper;
            Json::FastWriter writer;
            *out = writer.write(root);
#endif
        }

        //_data_x _oper _data_y
        bool Deserialization(std::string &inputbuffer)
        {
#ifdef Customize
            int left = inputbuffer.find(" ");
            if (left == std::string::npos)
                return false;
            int right = inputbuffer.rfind(" ");
            if (right == std::string::npos || right <= left)
                return false;

            _data_x = atoi(inputbuffer.substr(0, left).c_str());
            _data_y = atoi(inputbuffer.substr(right + space.size()).c_str());
            _oper = inputbuffer.substr(left + space.size(), right - left - space.size())[0];

            return true;
#else
            Json::Value root;
            Json::Reader reader;
            auto res = reader.parse(inputbuffer, root);
            if (res == false)
                return false;

            _data_x = root["datax"].asInt();
            _data_y = root["datay"].asInt();
            _oper = (char)root["oper"].asInt();
            return true;
#endif
        }

        int &GetX()
        {
            return _data_x;
        }

        int &GetY()
        {
            return _data_y;
        }

        char &GetOper()
        {
            return _oper;
        }

    private:
        int _data_x = 0;
        int _data_y = 0;
        char _oper = 0;
    };

    class Response
    {
    public:
        Response()
        {
        }

        Response(int result, int code)
            : _result(result), _code(code)
        {
        }

        void Serialization(std::string *out)
        {
#ifdef Customize
            *out = std::to_string(_result) + space + std::to_string(_code);
            return;
#else
            Json::Value root;
            root["result"] = _result;
            root["code"] = _code;
            Json::FastWriter writer;
            *out = writer.write(root);
#endif
        }

        //_data_x _oper _data_y
        bool Deserialization(std::string &inputbuffer)
        {
#ifdef Customize
            int pos = inputbuffer.find(" ");
            if (pos == std::string::npos)
                return false;

            _result = atoi(inputbuffer.substr(0, pos).c_str());
            _code = atoi(inputbuffer.substr(pos + space.size()).c_str());

            return true;
#else
            Json::Value root;
            Json::Reader reader;
            auto res = reader.parse(std::cin, root);
            if (res == false)
                return false;
            _result = root["result"].asInt();
            _code = root["code"].asInt();
#endif
        }

        int &GetResult()
        {
            return _result;
        }

        int &GetCode()
        {
            return _code;
        }

    private:
        int _result = 0;
        int _code = 0;
    };

    // 工厂模式
    class Factory
    {
    public:
        std::shared_ptr<Request> BulidRequest()
        {
            std::shared_ptr<Request> res = std::make_shared<Request>();
            return res;
        }

        std::shared_ptr<Request> BulidRequest(int x, char ch, int y)
        {
            std::shared_ptr<Request> res = std::make_shared<Request>(x, ch, y);
            return res;
        }

        std::shared_ptr<Response> BulidResponse()
        {
            std::shared_ptr<Response> res = std::make_shared<Response>();
            return res;
        }

        std::shared_ptr<Response> BulidResponse(int result, int code)
        {
            std::shared_ptr<Response> res = std::make_shared<Response>(result, code);
            return res;
        }
    };
}